home *** CD-ROM | disk | FTP | other *** search
- /*
- * @(#)uncgi.c 1.7 10/19/94
- *
- * Unescape all the fields in a form and stick them in the environment
- * so they can be used without awful machinations.
- *
- * Call with an ACTION such as:
- * http://foo.bar.com/cgi-bin/uncgi/myscript/extra/path/stuff
- *
- * Uncgi will run "myscript" from the cgi-bin directory, and set PATH_INFO
- * to "/extra/path/stuff".
- *
- * Environment variable names are "WWW_" plus the field name.
- *
- * Copyright 1994, Steven Grimm <koreth@hyperion.com>.
- *
- * Permission is granted to redistribute freely and use for any purpose,
- * commercial or private, so long as this copyright notice is retained
- * and the source code is included free of charge with any binary
- * distributions.
- */
- #include <stdio.h>
- #include <ctype.h>
-
- void *malloc(), *realloc();
- char *getenv();
-
- extern char *sys_errlist[];
- extern int errno;
-
- #define PREFIX "WWW_"
- #define ishex(x) (((x) >= '0' && (x) <= '9') || ((x) >= 'a' && (x) <= 'f') || \
- ((x) >= 'A' && (x) <= 'F'))
-
- /*
- * Convert two hex digits to a value.
- */
- static int
- htoi(s)
- unsigned char *s;
- {
- int value;
- char c;
-
- c = s[0];
- if (isupper(c))
- c = tolower(c);
- value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
-
- c = s[1];
- if (isupper(c))
- c = tolower(c);
- value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
-
- return (value);
- }
-
- /*
- * Get rid of all the URL escaping in a string. Modify it in place, since
- * the result will always be equal in length or smaller.
- */
- static void
- url_unescape(str)
- unsigned char *str;
- {
- unsigned char *dest = str;
-
- while (str[0])
- {
- if (str[0] == '+')
- dest[0] = ' ';
- else if (str[0] == '%' && ishex(str[1]) && ishex(str[2]))
- {
- dest[0] = (unsigned char) htoi(str + 1);
- str += 2;
- }
- else
- dest[0] = str[0];
-
- str++;
- dest++;
- }
-
- dest[0] = '\0';
- }
-
- /*
- * Print the start of an error message.
- */
- static void
- http_head()
- {
- puts("Content-type: text/html\n");
- puts("<html><head><title>Error!</title></head><body>");
- puts("<h1>An error has occurred while processing your request.</h1>");
- }
-
- /*
- * Print an HTML error string with the right HTTP header and exit.
- */
- static void
- html_perror(str)
- char *str;
- {
- http_head();
-
- puts("<p>The following error was encountered while processing");
- puts("your query.");
- puts("<blockquote>");
- printf("%s: %s\n", str, sys_errlist[errno]);
- puts("</blockquote>");
- puts("<p>Please try again later.");
- puts("</body></html>");
-
- exit(1);
- }
-
- /*
- * Stuff a URL-unescaped variable, with the prefix on its name, into the
- * environment. Uses the "=" from the CGI arguments. Putting an "=" in
- * a field name is probably a bad idea.
- *
- * If the variable is already defined, append a '#' to it along with the
- * new value.
- */
- static void
- stuffenv(var)
- char *var;
- {
- char *buf, *c, *oldval, *newval;
-
- url_unescape(var);
-
- /*
- * Allocate enough memory for the variable name and its value.
- */
- buf = malloc(strlen(var) + sizeof(PREFIX) + 1);
- if (buf == NULL)
- html_perror("stuffenv");
-
- strcpy(buf, PREFIX);
- strcpy(buf + sizeof(PREFIX) - 1, var); /* save a few cycles */
-
- /*
- * If, for some reason, there wasn't an = in the query string,
- * add one so the environment will be valid.
- */
- for (c = buf; *c != '\0'; c++)
- if (*c == '=')
- break;
- if (*c == '\0')
- c[1] = '\0';
-
- /*
- * Check for the presence of the variable.
- */
- *c = '\0';
- if (oldval = getenv(buf))
- {
- newval = malloc(strlen(oldval) + strlen(buf) + strlen(c+1) + 2);
- if (newval == NULL)
- html_perror("stuffenv: append");
-
- *c = '=';
- strcpy(newval, buf);
- strcat(newval, "#");
- strcat(newval, oldval);
- free(buf);
- }
- else
- {
- *c = '=';
- newval = buf;
- }
-
- putenv(newval);
- }
-
- /*
- * Scan a query string, stuffing variables into the environment. This
- * should ideally just use strtok(), but that's not available everywhere.
- */
- static void
- scanquery(q)
- char *q;
- {
- char *next = q;
-
- do {
- while (*next && *next != '&')
- next++;
- if (! *next)
- next = NULL;
- else
- *next = '\0';
-
- stuffenv(q);
- if (next)
- *next++ = '&';
- q = next;
- } while (q != NULL);
- }
-
- /*
- * Read a POST query from standard input into a dynamic buffer. Terminate
- * it with a null character.
- */
- static char *
- postread()
- {
- char *buf = NULL;
- int size = 0, sofar = 0, got;
-
- buf = getenv("CONTENT_TYPE");
- if (buf == NULL || strcmp(buf, "application/x-www-form-urlencoded"))
- {
- http_head();
- puts("<p>No content type was passed to uncgi.</body></html>");
- exit(1);
- }
-
- buf = getenv("CONTENT_LENGTH");
- if (buf == NULL)
- {
- http_head();
- puts("<p>The server did not tell uncgi how long the request");
- puts("was.</body></html>");
- exit(1);
- }
-
- size = atoi(buf);
- buf = malloc(size);
- if (buf == NULL)
- html_perror("postread");
- do
- {
- got = fread(buf + sofar, 1, size - sofar, stdin);
- sofar += got;
- } while (got && sofar < size);
-
- buf[sofar] = '\0';
-
- return (buf);
- }
-
- /*
- * Run a shell script. We use this instead of the OS's "#!" mechanism
- * because that mechanism doesn't work too well on SVR3-based systems.
- */
- void
- runscript(shell, script)
- char *shell, *script;
- {
- char *argvec[4], *space;
- int pos;
-
- if (shell[0] != '/')
- return;
-
- pos = strlen(shell) - 1;
- if (shell[pos] == '\n')
- shell[pos] = '\0';
-
- argvec[0] = shell;
-
- /*
- * See if there's an argument string. strchr() isn't available
- * everywhere, so do it ourselves.
- */
- for (space = shell; *space; space++)
- if (*space == ' ')
- break;
-
- if (*space)
- {
- *space = '\0';
- argvec[1] = space + 1;
- argvec[2] = script;
- argvec[3] = NULL;
- }
- else
- {
- argvec[1] = script;
- argvec[2] = NULL;
- }
-
- execv(shell, argvec);
- /* Fall back to main() on error. */
- }
-
- /*
- * Main program, optionally callable as a library function.
- */
- void
- #ifdef NO_MAIN
- uncgi()
- #else
- main(argc, argv)
- int argc;
- char **argv;
- #endif
- {
- char *query, *program, *pathinfo, *newpathinfo, *method;
- char *ptrans, *ptend;
- char *argvec[2], shellname[200];
- int proglen;
- FILE *fp;
-
- /*
- * First, get the query string, wherever it is, and stick its
- * component parts into the environment. Allow combination
- * GET and POST queries, even though that's a bit strange.
- */
- query = getenv("QUERY_STRING");
- if (query != NULL && strlen(query))
- scanquery(query);
-
- method = getenv("REQUEST_METHOD");
- if (method != NULL && ! strcmp(method, "POST"))
- {
- query = postread();
- if (query[0] != '\0')
- scanquery(query);
- }
-
- if (query == NULL)
- {
- http_head();
- puts("<p>The 'uncgi' program couldn't find a query to");
- puts("process.\n</body></html>");
- exit(1);
- }
-
- #ifndef NO_MAIN
- /*
- * Now figure out which program the caller *really* wants, and adjust
- * PATH_INFO and PATH_TRANSLATED to look right to that program.
- */
- pathinfo = getenv("PATH_INFO");
- if (pathinfo != NULL && pathinfo[0])
- {
- proglen = 1; /* path_info always starts with '/' */
-
- while (pathinfo[proglen] && pathinfo[proglen] != '/')
- proglen++;
-
- program = malloc(proglen + sizeof(CGI_BIN));
- if (program == NULL)
- html_perror("program");
- strcpy(program, CGI_BIN);
- strncat(program + sizeof(CGI_BIN) - 1, pathinfo, proglen);
-
- /*
- * Strip "program" from PATH_TRANSLATED.
- * XXX - this depends on strcpy() copying front to back.
- */
- ptrans = getenv("PATH_TRANSLATED");
- if (ptrans != NULL)
- {
- ptend = ptrans + strlen(ptrans) - strlen(pathinfo);
- strcpy(ptend, ptend + proglen);
- ptend = malloc(strlen(ptrans) +
- sizeof("PATH_TRANSLATED="));
- strcpy(ptend, "PATH_TRANSLATED=");
- strcat(ptend, ptrans);
- putenv(ptend);
- }
-
- pathinfo += proglen;
- newpathinfo = malloc(strlen(pathinfo) + sizeof("PATH_INFO="));
- if (newpathinfo == NULL)
- html_perror("newpathinfo");
- strcpy(newpathinfo, "PATH_INFO=");
- strcat(newpathinfo, pathinfo);
- putenv(newpathinfo);
-
-
- }
- else
- {
- /*
- * No PATH_INFO means no program to run.
- */
- http_head();
- puts("<p>Whoever wrote this form doesn't know how to use");
- puts("the 'uncgi' program, because they didn't tell it");
- puts("what to run.");
- puts("<h5>(Bummer.)</h5></body></html>");
-
- exit(0);
- }
-
- /*
- * SVR3-based systems seem to have trouble running shell scripts.
- * So if that's what this is, run its shell explicitly.
- */
- fp = fopen(program, "r");
- if (fp != NULL)
- {
- if (fgets(shellname, sizeof(shellname), fp) != NULL)
- {
- fclose(fp);
- if (shellname[0] == '#' && shellname[1] == '!')
- runscript(shellname + 2, program);
- }
- fclose(fp);
- }
-
- /*
- * Now execute the program.
- */
- argvec[0] = program;
- argvec[1] = NULL;
- execv(program, argvec);
-
- /*
- * If we get here, the exec failed.
- */
- html_perror(program);
- #endif
- }
-